package tool

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/kr/pretty"
	"github.com/pkg/errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"time"
)

// 要支持http结果自定义解析，必须是该类型的子类型
type CustomHttpResponse interface {
	CheckRsp() error
}
type BaseHttpResponse struct {
	//自定义http Response的基类
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func (b *BaseHttpResponse) CheckRsp() error {
	return nil
}

type HttpReqWrapper struct {
	Ctx         context.Context
	Method      string
	Url         string
	QueryParam  map[string]interface{}
	HeaderParam map[string]string
	//#目前支持 map类型
	BodyParam map[string]interface{}
	RetryNum  int
	Interval  int64
}

// 将用户指定的queryparam 合并到url中
func (hw *HttpReqWrapper) reGenerateUrl() (string, error) {
	if parseResult, err := url.Parse(hw.Url); err != nil {
		return "", fmt.Errorf("解析request失败:%v", err)
	} else {
		//合并，通过map指定的优先级高
		query := parseResult.Query()
		for k, v := range hw.QueryParam {
			query.Set(k, fmt.Sprintf("%v", v))
		}
		parseResult.RawQuery = query.Encode()
		return parseResult.String(), nil
	}
}

func (hw *HttpReqWrapper) getHttpRequest() (req *http.Request, err error) {
	var bodyIO io.Reader
	//生成body参数
	if hw.BodyParam != nil {

		bodyParam, err := json.Marshal(hw.BodyParam)
		if err != nil {
			return nil, err
		}
		bodyIO = bytes.NewReader(bodyParam)
	}
	//生成query_param
	newUrl := ""
	if newUrl, err = hw.reGenerateUrl(); err != nil {
		return nil, err
	}
	req, err = http.NewRequest(hw.Method, newUrl, bodyIO)
	//生成header——param
	for k, v := range hw.HeaderParam {
		req.Header.Set(k, fmt.Sprintf("%v", v))
	}
	return req, err
}

func DoHttpRequest(reqWrapper *HttpReqWrapper, expectRsp CustomHttpResponse) (err error) {
	client := http.DefaultClient
	client.Timeout = time.Second * 10
	req, err := reqWrapper.getHttpRequest()
	if err != nil {
		return err
	}
	loop := -1
	var response *http.Response
	for loop < reqWrapper.RetryNum {
		response, err = client.Do(req)
		if err == nil && response.StatusCode == 200 {
			//用户自定义的rsp的响应检查
			rspData, err := ioutil.ReadAll(response.Body)
			if err != nil {
				return errors.New(fmt.Sprintf("用户自定义校验失败: %v", err.Error()))
			}
			log.Printf("请求执行成功, response=%v", string(rspData))
			if err = json.Unmarshal(rspData, expectRsp); err != nil {
				return errors.New(fmt.Sprintf("用户自定义校验失败: %v", err.Error()))
			}
			err = expectRsp.CheckRsp()
			if err == nil {
				return nil
			}
		}
		time.Sleep(time.Duration(reqWrapper.Interval) * time.Millisecond)
		loop++
	}

	msg := ""
	if response != nil {
		all, _ := io.ReadAll(response.Body)
		msg = string(all)
	}
	//错误还可以进一步包装一下
	if response != nil {
		msgParam := map[string]interface{}{
			"method":           reqWrapper.Method,
			"url":              reqWrapper.Url,
			"status_code":      response.StatusCode,
			"status":           response.Status,
			"header_param":     reqWrapper.HeaderParam,
			"query_param":      reqWrapper.QueryParam,
			"body_param":       reqWrapper.BodyParam,
			"response_content": msg,
		}
		msg = pretty.Sprint(msgParam)
	}
	return errors.Errorf(msg)
}

func verifyResponseStruct(tp interface{}) error {
	valueOf := reflect.ValueOf(tp)
	if valueOf.Kind() != reflect.Ptr {
		return errors.Errorf("kind of param not ptr type: %v", valueOf.Kind().String())
	}
	hasCode := false
	hasMsg := false
	for i := 0; i < valueOf.Type().NumField(); i++ {
		if valueOf.Field(i).String() == "Code" {
			hasCode = true
		}
		if valueOf.Field(i).String() == "Msg" {
			hasMsg = true
		}
	}
	if !hasMsg {
		return errors.Errorf("type of %v mush have field [Msg string]", valueOf.Type().String())
	}
	if !hasCode {
		return errors.Errorf("type of %v mush have field [Code int]", valueOf.Type().String())
	}
	return nil
}
